package com.example.java.generic;

import java.io.Serializable;
import java.util.ArrayList;

public class GenericMethod<K> {

    private K data;

    /**
     * 泛型方法,有泛型类型声明
     *
     * @param ts
     * @param <T>
     * @return
     */
    public <T> int getLenth(T... ts) {
        return ts.length;
    }

    /**
     * 不是泛型方法，没有泛型类型声明
     *
     * @return
     */
    public K getData() {
        return data;
    }

    /**
     * T extends Comparable中
     * T表示应该绑定类型的子类型，Comparable表示绑定类型，子类型和绑定类型可以是类也可以是接口。
     * 如果这个时候，我们试图传入一个没有实现接口Comparable的类的实例，将会发生编译错误。
     * 同时extends左右都允许有多个，如 T,V extends Comparable & Serializable
     * 注意限定类型中，只允许有一个类，而且如果有类，这个类必须是限定列表的第一个。
     * 这种类的限定既可以用在泛型方法上也可以用在泛型类上。
     */
    public <T extends Comparable & Serializable> T min(T a, T b) {
        return a.compareTo(b) > 0 ? b : a;
    }

    public <T extends Comparable & Serializable> T max(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }

    /**
     * 泛型中的约束和局限性
     */
    public static void main(String[] args) {
        //1.不能用基本类型实例化类型参数
        //GenericMethod<double> genericMethod = new GenericMethod<>();
        //2.运行时类型查询只适用于原始类型
        GenericMethod<String> stringGenericMethod = new GenericMethod<>();
        GenericMethod<Double> stringGenericMethod2 = new GenericMethod<>();
//        if(stringGenericMethod instanceof GenericMethod<String>){ }
//        if(stringGenericMethod instanceof GenericMethod<T>){ }
        System.out.println(stringGenericMethod.getClass() == stringGenericMethod2.getClass());
        ////3.泛型类的静态上下文中类型变量失效
        /*
        静态域或者方法里不能引用类型变量。
        Why？
        不能在静态域或方法中引用类型变量。
        因为泛型是要在对象创建的时候才知道是什么类型的，
        而对象创建的代码执行先后顺序是static的部分，然后才是构造函数等等。
        所以在对象初始化之前static的部分已经执行了，如果你在静态部分引用的泛型，
        那么毫无疑问虚拟机根本不知道是什么东西，因为这个时候类还没有初始化。
         */
//        private static K cannot;
        //静态方法  本身是泛型方法就可以
//        private static <T> void test(){}

        //4.不能创建参数化类型的数组
//        GenericMethod<String>[] a;允许
//        GenericMethod<String>[] b = new GenericMethod<String>[10];不允许

        //5.不能实例化类型变量
//        new K();不允许
        //6.不能捕获泛型类的实例

    }

    //接6 泛型类不能继承Exception/Throwable
//    private class Problem<T> extends Exception{ }
    //不能捕获泛型类对象
//    public <T extends Throwable> void doWork(T t){
//        try {
//
//        }catch (T e){
//
//        }
//    }
    //这样可以
    public <T extends Throwable> void doWork(T t) throws T {
        try {

        } catch (Throwable e) {
            throw t;
        }
    }

    /*
    泛型类型的继承规则
    请问GenericMethod<Employee>和GenericMethod<Worker>是继承关系吗？
    答案：不是，他们之间没有什么关系
    但是泛型类可以继承或者扩展其他泛型类，比如List和ArrayList
     */
    public static class  Employee{}
    public static class Worker extends Employee{}
    public static class ExtendGeneric<K> extends GenericMethod<K>{}

    /*
通配符类型
？ extends X  表示类型的上界，类型参数是X的子类
set方法是不允许被调用的
get方法则没问题，会返回一个Fruit类型的值。
为何？
？ extends X  表示类型的上界，类型参数是X的子类，那么可以肯定的说，get方法返回的一定是个X（不管是X或者X的子类）编译器是可以确定知道的。
但是set方法只知道传入的是个X，至于具体是X的那个子类，不知道。
总结：主要用于安全地访问数据，可以访问X及其子类型，并且不能写入非null的数据。

？ super X  表示类型的下界，类型参数是X的超类
表示传递给方法的参数，必须是X的超类（包括X本身）
set方法可以被调用的，且能传入的参数只能是X或者X的子类
？ super  X  表示类型的下界，类型参数是X的超类（包括X本身），那么可以肯定的说，get方法返回的一定是个X的超类，那么到底是哪个超类？不知道，但是可以肯定的说，Object一定是它的超类，所以get方法返回Object。编译器是可以确定知道的。对于set方法来说，编译器不知道它需要的确切类型，但是X和X的子类可以安全的转型为X。
总结：主要用于安全地写入数据，可以写入X及其子类型。

     */
}
